package com.niodata.dp.web.module.task;

import com.niodata.dp.core.ConfigurationKeys;
import com.niodata.dp.core.Constants;
import com.niodata.dp.core.OwnerType;
import com.niodata.dp.core.exception.InvalidParamException;
import com.niodata.dp.core.resource.ResourceDetail;
import com.niodata.dp.core.resource.ResourceManager;
import com.niodata.dp.core.resource.ResourceType;
import com.niodata.dp.core.resource.ResourceType.PluginType;
import com.niodata.dp.core.service.SystemServices;
import com.niodata.dp.core.usermgr.model.UserInfo;
import com.niodata.dp.core.usermgr.op.GroupInfoService;
import com.niodata.dp.task.entity.PluginCategoryCountSta;
import com.niodata.dp.task.entity.PluginDocument;
import com.niodata.dp.task.entity.PluginDocumentView;
import com.niodata.dp.task.entity.PluginPackage;
import com.niodata.dp.task.entity.PluginPackage.PluginStatus;
import com.niodata.dp.task.entity.PluginPackageView;
import com.niodata.dp.task.entity.PluginSysCategory;
import com.niodata.dp.task.entity.v2.PackageParamsV2;
import com.niodata.dp.task.entity.v2.PluginPackageV2;
import com.niodata.dp.task.entity.v2.PluginPackageViewV2;
import com.niodata.dp.task.service.PluginManager2;
import com.niodata.dp.task.service.PluginManagerV2;
import com.niodata.dp.util.HttpCaller;
import com.niodata.dp.util.IdUtil;
import com.niodata.dp.web.PageInfo;
import com.niodata.dp.web.module.base.BaseController;
import com.niodata.dp.web.security.ContextUtil;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletResponse;
import org.apache.http.HttpStatus;
import org.apache.ibatis.annotations.Param;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * plugin controller v2.
 *
 * @author tiger
 */
@RestController("PackageController2")
@RequestMapping("/v1/plugin")
public class PluginPackageController2 extends BaseController {

  private static final Logger logger = Logger.getLogger(PluginPackageController2.class);
  @Autowired
  @Qualifier("PluginManagerV2Impl")
  private PluginManagerV2 pluginManagerV2;
  @Autowired
  @Qualifier("PluginManager2Impl")
  private PluginManager2 pluginManager;

  @Autowired
  @Qualifier("DpUserMgrGroup")
  private GroupInfoService groupInfoService;
  @Autowired
  @Qualifier("ResourceManagerImpl")
  private ResourceManager resourceManager;

  @Value("${" + ConfigurationKeys.PLUGIN_HUB_URL + ":http://dp.ndp.com}")
  private String pluginHub;

  private static boolean isValidUri(String uriStr) {
    try {
      URI uri = new URI(uriStr);
      return true;
    } catch (URISyntaxException e) {
      return false;
    }
  }

  /**
   * 分页查询插件列表信息.
   *
   * @param pageIndex 查询页码，默认第一页
   * @param pageSize 每页显示数，默认20
   * @return 查询的信息
   */
  @RequestMapping(value = {"/view/list"}, method = {RequestMethod.GET})
  public Object getPackageList(
        @RequestParam(value = "pageIndex", required = false, defaultValue = "1") Integer pageIndex,
        @RequestParam(value = "pageSize", required = false, defaultValue = "20") Integer pageSize,
        @RequestParam(value = "sysCategory", required = false) String sysCategory,
        @RequestParam(value = "funcCategory", required = false) String funcCategory,
        @RequestParam(value = "nameContains", required = false) String nameContains) {
    nameContains = nameContains != null && nameContains.length() > 0 ? nameContains : null;
    UserInfo userInfo = ContextUtil.getCurrentUser();
    List<PluginPackageView> pluginList =
          this.pluginManager.getPlugins(userInfo.getUserId(),
                sysCategory, funcCategory, nameContains,
                (pageIndex - 1) * pageSize,
                pageSize + 1
          );

    PageInfo pageInfo = new PageInfo();
    pageInfo.setHasMore(pluginList.size() > pageSize);
    pageInfo.setPageIndex(pageIndex);
    pageInfo.setStart((pageIndex - 1) * pageSize + 1);
    if (pluginList.size() > pageSize) {
      pluginList.remove(pluginList.size() - 1);
    }
    pageInfo.setStop((pageIndex - 1) * pageSize + pluginList.size());
    return this.getPagedResult(pluginList, pageInfo);
  }

  @RequestMapping(value = "/view/plugin", method = RequestMethod.GET)
  @ResponseBody
  public Object getPackageByNameVersion(String name, String version) {
    UserInfo userInfo = ContextUtil.getCurrentUser();
    return pluginManager.getPluginByNameVersion(userInfo.getUserId(), name, version);
  }

  /**
   * 返回功能分类列表map.
   *
   * @param sysCategory sys category
   * @return func category map
   */
  @RequestMapping(value = "view/categories", method = RequestMethod.GET)
  @ResponseBody
  public Object getPluginFuncCategories(@RequestParam("sysCategory") String sysCategory) {
    List<String> categories = this.pluginManager.getFuncCategories(sysCategory);
    Map<String, String> map = new HashMap<>();
    for (String cat : categories) {
      map.put(cat, cat);
    }
    return getResult(map);
  }

  @RequestMapping(value = "view/category_sta", method = RequestMethod.GET)
  @ResponseBody
  public Object getPluginCategoryCount() {
    UserInfo userInfo = ContextUtil.getCurrentUser();
    PluginCategoryCountSta categorysta =
          this.pluginManager.getPluginCategoryCount(userInfo.getUserId());
    return categorysta;
  }

  /**
   * 查询插件详细信息.
   *
   * @param pkgId 插件ID
   * @param name plugin name
   * @param version plugin version
   * @return 对应的插件信息
   */
  @RequestMapping(value = {"/view/detail"}, method = RequestMethod.GET)
  public Object getPackageDetail(@RequestParam(value = "pkgId", required = false) String pkgId,
        @RequestParam(value = "name", required = false) String name,
        @RequestParam(value = "version", required = false) String version,
        HttpServletResponse response) {
    PluginPackage pluginPackage = null;
    if (pkgId != null) {
      pluginPackage = this.pluginManager.getPlugin(pkgId);
    } else if (name != null && version != null) {
      UserInfo userInfo = ContextUtil.getCurrentUser();
      pluginPackage = this.pluginManager
            .getPluginByNameVersion(userInfo.getUserId(), name, version);
    } else {
      response.setStatus(HttpStatus.SC_BAD_REQUEST);
      return getResult("param error");
    }
    return this.getResult(pluginPackage);
  }

  /**
   * 查询插件详细信息.
   *
   * @param pkgId 插件ID
   * @param name plugin name
   * @param version plugin version
   * @return 对应的插件信息
   */
  @RequestMapping(value = {"/view/detail/v2"}, method = RequestMethod.GET)
  public Object getPackageDetailV2(@RequestParam(value = "pkgId", required = false) String pkgId,
        @RequestParam(value = "name", required = false) String name,
        @RequestParam(value = "version", required = false) String version,
        HttpServletResponse response) {
    PluginPackageV2 pluginPackage = null;
    if (pkgId != null) {
      pluginPackage = this.pluginManagerV2.getPlugin(pkgId);
    } else if (name != null && version != null) {
      UserInfo userInfo = ContextUtil.getCurrentUser();
      pluginPackage = this.pluginManagerV2
            .getPluginByNameVersion(userInfo.getUserId(), name, version);
    } else {
      response.setStatus(HttpStatus.SC_BAD_REQUEST);
      return getResult("param error");
    }
    //遗留插件适配,将运行时参数和业务参数分为两组
    pluginPackage.setParams(PackageParamsV2.buildGroupedParams(pluginPackage.getParams(),
          pluginPackage.getJobType(), pluginPackage.getPkgLang()));
    PackageParamsV2.filterOutUserNotSetAble(pluginPackage.getParams());
    return this.getResult(pluginPackage);
  }


  @RequestMapping(value = "/admin/update/desc", method = RequestMethod.PUT)
  public Object updateDescription(@RequestParam("id") String id,
        @RequestParam("description") String description) {
    PluginPackageV2 pluginPackage = this.pluginManagerV2.getPlugin(id);
    pluginPackage.setDescription(description);
    this.pluginManagerV2.update(pluginPackage);
    return getResult("ok");
  }

  /**
   * update package.
   *
   * @param id pkgid
   * @param status status
   * @param description desc
   * @param sysCategory syscate
   * @param funcCategory funccate
   * @param file file
   * @param tags tags
   * @return msg
   * @throws IOException ioe
   */
  @RequestMapping(value = "/admin/update", method = {RequestMethod.PUT, RequestMethod.POST})
  public Object updatePluginPackage(
        @RequestParam("id") String id,
        @RequestParam(value = "status", defaultValue = "Dev") PluginStatus status,
        @RequestParam(value = "description", defaultValue = "") String description,
        @RequestParam("sysCategory") PluginSysCategory sysCategory,
        @RequestParam("funcCategory") String funcCategory,
        @RequestParam(value = "file", required = false) MultipartFile file,
        @RequestParam(value = "tags", defaultValue = "") String tags,
        @RequestParam(value = "publicOn", defaultValue = "true", required = true) boolean publicOn)
        throws Exception {
    PluginPackage pluginPackage = this.pluginManager.getPlugin(id);
    if (tags == null) {
      tags = "";
    }
    pluginPackage.setTags(tags);
    pluginPackage.setStatus(status);
    pluginPackage.setFuncCategory(funcCategory);
    pluginPackage.setSysCategory(sysCategory);
    pluginPackage.setPublicOn(publicOn);
    if (file != null) {
      PluginPackage newPkg =
            pluginManager.savePluginFile(file.getInputStream(),
                  pluginPackage.getPkgName(), pluginPackage.getPkgVersion());
      if (pluginPackage.getDescription().equals(description)) {
        pluginPackage.setDescription(newPkg.getDescription());
      } else {
        pluginPackage.setDescription(description);
      }
      pluginPackage.setDescription(description);
      pluginPackage.setParams(newPkg.getParams());
      pluginPackage.setOutParams(newPkg.getOutParams());
      pluginPackage.setJobType(newPkg.getJobType());
      pluginPackage.setPkgLang(newPkg.getPkgLang());
      pluginPackage.setPkgType(newPkg.getPkgType());
      pluginPackage.setPkgPath(newPkg.getPkgPath());
      pluginPackage.setChanges(newPkg.getChanges());
      pluginPackage.setMaintainers(newPkg.getMaintainers());
      pluginPackage.setMinPlatformVersion(newPkg.getMinPlatformVersion());
      pluginPackage.setDefaultParams(newPkg.getDefaultParams());
      pluginPackage.setDependencies(newPkg.getDependencies());
      pluginPackage.setScmType(newPkg.getScmType());
      pluginPackage.setSourceRepo(newPkg.getSourceRepo());
      pluginPackage.setFailRetry(newPkg.isFailRetry());
    }
    this.pluginManager.update(pluginPackage);
    String doc = pluginManager.genUsageMd(pluginPackage);
    PluginDocument document = new PluginDocument();
    document.setDocument(doc);
    document.setName(pluginPackage.getPkgName());
    document.setPkgId(pluginPackage.getPkgId());
    document.setVersion(pluginPackage.getPkgVersion());
    this.pluginManager.updateDocument(document);

    return getResult("ok");
  }


  /**
   * update package.
   *
   * @param id pkgid
   * @param status status
   * @param description desc
   * @param sysCategory syscate
   * @param funcCategory funccate
   * @param file file
   * @param tags tags
   * @return msg
   * @throws IOException ioe
   */
  @RequestMapping(value = "/admin/update/v2", method = {RequestMethod.PUT, RequestMethod.POST})
  public Object updatePluginPackageV2(
        @RequestParam("id") String id,
        @RequestParam(value = "status", defaultValue = "Dev") PluginPackageV2.PluginStatus status,
        @RequestParam(value = "description", defaultValue = "") String description,
        @RequestParam("sysCategory") PluginSysCategory sysCategory,
        @RequestParam("funcCategory") String funcCategory,
        @RequestParam(value = "file", required = false) MultipartFile file,
        @RequestParam(value = "tags", defaultValue = "") String tags,
        @RequestParam(value = "publicOn", defaultValue = "true", required = true) boolean publicOn)
        throws Exception {
    PluginPackageV2 pluginPackage = this.pluginManagerV2.getPlugin(id);
    if (tags == null) {
      tags = "";
    }
    pluginPackage.setTags(tags);
    pluginPackage.setStatus(status);
    pluginPackage.setFuncCategory(funcCategory);
    pluginPackage.setSysCategory(sysCategory);
    pluginPackage.setPublicOn(publicOn);
    if (file != null) {
      PluginPackageV2 newPkg =
            pluginManagerV2.savePluginFile(file.getInputStream(),
                  pluginPackage.getPkgName(), pluginPackage.getPkgVersion());
      if (pluginPackage.getDescription().equals(description)) {
        pluginPackage.setDescription(newPkg.getDescription());
      } else {
        pluginPackage.setDescription(description);
      }
      pluginPackage.setDescription(description);
      pluginPackage.setParams(newPkg.getParams());
      pluginPackage.setOutParams(newPkg.getOutParams());
      pluginPackage.setJobType(newPkg.getJobType());
      pluginPackage.setPkgLang(newPkg.getPkgLang());
      pluginPackage.setPkgType(newPkg.getPkgType());
      pluginPackage.setPkgPath(newPkg.getPkgPath());
      pluginPackage.setChanges(newPkg.getChanges());
      pluginPackage.setMaintainers(newPkg.getMaintainers());
      pluginPackage.setMinPlatformVersion(newPkg.getMinPlatformVersion());
      pluginPackage.setDefaultParams(newPkg.getDefaultParams());
      pluginPackage.setDependencies(newPkg.getDependencies());
      pluginPackage.setScmType(newPkg.getScmType());
      pluginPackage.setSourceRepo(newPkg.getSourceRepo());
      pluginPackage.setFailRetry(newPkg.isFailRetry());
    }
    this.pluginManagerV2.update(pluginPackage);
    String doc = pluginManagerV2.genUsageMd(pluginPackage);
    PluginDocument document = new PluginDocument();
    document.setDocument(doc);
    document.setName(pluginPackage.getPkgName());
    document.setPkgId(pluginPackage.getPkgId());
    document.setVersion(pluginPackage.getPkgVersion());
    this.pluginManager.updateDocument(document);

    return getResult("ok");
  }

  /**
   * install plugin from remote.
   *
   * @param isPublic is public
   * @return status
   */
  @RequestMapping(value = "/admin/add/remote", method = RequestMethod.POST)
  public Object createPackageByRemote(@Param("pkgName") String pkgName,
        @Param("pkgVersion") String pkgVersion,
        @RequestParam(value = "isPublic", defaultValue = "true", required = true) boolean isPublic)
        throws Exception {
    String uuid = UUID.randomUUID().toString();
    File zipFile = new File(uuid);
    InputStream inputStream = null;
    try {
      String remoteUrl = pluginHub + "/hub/v1/plugin/view/download";
      Map<String, String> params = new HashMap<>();
      params.put("name", pkgName);
      params.put("version", pkgVersion);
      HttpCaller.httpGetFile(remoteUrl, params, null, 10 * 60, null, zipFile);
      inputStream = new FileInputStream(zipFile);
      UserInfo userInfo = ContextUtil.getCurrentUser();
      long ownerId =
            groupInfoService.getOwnerOrMasterGroups(userInfo.getUserId()).get(0).getGroupId();
      Map<String, Object> map = getPluginDetailFromHub(pkgName, pkgVersion);
      Object obj = installPackage(pkgName, pkgVersion, inputStream, isPublic, "GROUP",
            ownerId,
            PluginSysCategory.valueOf(map.get("sysCategory").toString()),
            map.get("funcCategory").toString(),
            map.get("tags") != null ? map.get("tags").toString() : "",
            PluginStatus.valueOf(map.get("status").toString()));
      return obj;
    } catch (Exception e) {
      throw e;
    } finally {
      if (inputStream != null) {
        inputStream.close();
      }
      zipFile.delete();
    }
  }

  /**
   * create plugin packae.
   *
   * @param ownerId owner
   * @param file pkg file
   * @return map
   * @throws IOException e
   */
  @RequestMapping(value = {"/admin/add/local", "/admin/add"}, method = {RequestMethod.POST})
  public Object createPackage(
        @RequestParam("pkgName") String pkgName,
        @RequestParam("pkgVersion") String pkgVersion,
        @RequestParam(value = "ownerId", required = true) Long ownerId,
        @RequestParam(value = "ownerType", defaultValue = "GROUP") String ownerTypeStr,
        @RequestParam("file") MultipartFile file,
        @RequestParam(value = "status", defaultValue = "Dev") PluginStatus status,
        @RequestParam("sysCategory") PluginSysCategory sysCategory,
        @RequestParam(value = "funcCategory", defaultValue = "") String funcCategory,
        @RequestParam(value = "tags", defaultValue = "") String tags,
        @RequestParam(value = "publicOn", defaultValue = "true", required = true) boolean publicOn)
        throws Exception {
    if (this.pluginManager.exists(pkgName, pkgVersion)) {
      throw new InvalidParamException(String.format("插件 %s:%s 已存在", pkgName, pkgVersion));
    }
    this.operationAccessControl.checkServicePermission(ContextUtil.getCurrentUser().getUserId(),
          OwnerType.valueOf(ownerTypeStr), ownerId,
          SystemServices.getSystemService(ResourceType.Plugin.getTypeName()));
    try {
      return installPackage(pkgName.trim(), pkgVersion.trim(), file.getInputStream(), publicOn,
            ownerTypeStr, ownerId, sysCategory, funcCategory, tags, status);
    } catch (Exception ex) {
      pluginManager.removePlugin(pkgName, pkgVersion);
      throw ex;
    }
  }

  /**
   * create plugin packae.
   *
   * @param ownerId owner
   * @param file pkg file
   * @return map
   * @throws IOException e
   */
  @RequestMapping(value = {"/admin/add/local/v2", "/admin/add/v2"}, method = {RequestMethod.POST})
  public Object createPackageV2(
        @RequestParam("pkgName") String pkgName,
        @RequestParam("pkgVersion") String pkgVersion,
        @RequestParam(value = "ownerId", required = true) Long ownerId,
        @RequestParam(value = "ownerType", defaultValue = "GROUP") String ownerTypeStr,
        @RequestParam("file") MultipartFile file,
        @RequestParam(value = "status", defaultValue = "Dev") PluginPackageV2.PluginStatus status,
        @RequestParam("sysCategory") PluginSysCategory sysCategory,
        @RequestParam(value = "funcCategory", defaultValue = "") String funcCategory,
        @RequestParam(value = "tags", defaultValue = "") String tags,
        @RequestParam(value = "publicOn", defaultValue = "true", required = true) boolean publicOn)
        throws Exception {
    if (this.pluginManagerV2.exists(pkgName, pkgVersion)) {
      throw new InvalidParamException(String.format("插件 %s:%s 已存在", pkgName, pkgVersion));
    }
    this.operationAccessControl.checkServicePermission(ContextUtil.getCurrentUser().getUserId(),
          OwnerType.valueOf(ownerTypeStr), ownerId,
          SystemServices.getSystemService(ResourceType.Plugin.getTypeName()));
    try {
      return installPackageV2(pkgName.trim(), pkgVersion.trim(), file.getInputStream(), publicOn,
            ownerTypeStr, ownerId, sysCategory, funcCategory, tags, status);
    } catch (Exception ex) {
      pluginManager.removePlugin(pkgName, pkgVersion);
      throw ex;
    }
  }

  private Map<String, Object> installPackageV2(String pkgName,
        String pkgVersion, InputStream inputStream,
        boolean publicOn, String ownerTypeStr,
        Long ownerId,
        PluginSysCategory sysCategory,
        String funcCategory,
        String tags,
        PluginPackageV2.PluginStatus status) throws Exception {
    PluginPackageV2 plugin = pluginManagerV2.savePluginFile(inputStream, pkgName, pkgVersion);
    plugin.setPublicOn(publicOn);
    List<String> erros = new ArrayList<>();

    if (plugin.getPkgName() == null) {
      erros.add("插件名称不可为空");
    }

    if (!pkgName.equals(plugin.getPkgName())) {
      erros.add("插件名称与meta.json中不一致");
    }
    if (!pkgVersion.equals(plugin.getPkgVersion())) {
      erros.add("插件版本与meta.json中不一致");
    }

    if (plugin.getPkgName().length() > 20) {
      erros.add("插件名称最多20字");
    }

    if (plugin.getDescription() == null || plugin.getDescription().length() < 10) {
      erros.add("描述信息不少于10字");
    }

    if (plugin.getMaintainers() == null) {
      erros.add("maintainers不能为空");
    }
    if (plugin.getSourceRepo() == null || !isValidUri(plugin.getSourceRepo())) {
      erros.add("sourceRepo地址格式错误");
    }
    if (!plugin.getPkgVersion().matches(Constants.PLUGIN_VERSION_PATTERN)) {
      logger.error("plugin version error: " + plugin.getPkgVersion());
      erros.add("插件版本格式有误，正确格式为：${MAIN_VERSION}-${PLATVERSION}");
    }

    if (erros.size() > 0) {
      pluginManager.removePluginFile(plugin.getPkgName(), plugin.getPkgVersion());
      String error = String.join("\r\n",erros);
      throw new InvalidParamException(error);
    }

    OwnerType ownerType = OwnerType.valueOf(ownerTypeStr);
    if (ownerType == OwnerType.GROUP) {
      plugin.setOwnerType(OwnerType.GROUP);
      plugin.setOwnerId(ownerId);
    }
    plugin.setTags(tags);
    plugin.setSysCategory(sysCategory);
    plugin.setFuncCategory(funcCategory);
    plugin.setPkgId(IdUtil.uuid());
    plugin.setCreator(ContextUtil.getCurrentUser().getLoginName());
    plugin.setCreateTime(new Date());
    plugin.setUpdateTime(plugin.getCreateTime());
    plugin.setStatus(status);
    //String doc = PluginDocumentGenerator.genDocument(plugin);
    String doc = null;
    try {
      doc = pluginManagerV2.genUsageMd(plugin);
    } catch (Exception e) {
      pluginManager.removePluginFile(plugin.getPkgName(), plugin.getPkgVersion());
      throw e;
    }

    pluginManagerV2.savePlugin(plugin, doc);
    return getResult(true);
  }

  private Map<String, Object> installPackage(String pkgName,
        String pkgVersion, InputStream inputStream,
        boolean publicOn, String ownerTypeStr,
        Long ownerId,
        PluginSysCategory sysCategory,
        String funcCategory,
        String tags,
        PluginStatus status) throws Exception {
    PluginPackage plugin = pluginManager.savePluginFile(inputStream, pkgName, pkgVersion);
    plugin.setPublicOn(publicOn);
    List<String> erros = new ArrayList<>();

    if (plugin.getPkgName() == null) {
      erros.add("插件名称不可为空");
    }

    if (!pkgName.equals(plugin.getPkgName())) {
      erros.add("插件名称与meta.json中不一致");
    }
    if (!pkgVersion.equals(plugin.getPkgVersion())) {
      erros.add("插件版本与meta.json中不一致");
    }

    if (plugin.getPkgName().length() > 20) {
      erros.add("插件名称最多20字");
    }

    if (plugin.getDescription() == null || plugin.getDescription().length() < 10) {
      erros.add("描述信息不少于10字");
    }

    if (plugin.getMaintainers() == null) {
      erros.add("maintainers不能为空");
    }
    if (plugin.getSourceRepo() == null || !isValidUri(plugin.getSourceRepo())) {
      erros.add("sourceRepo地址格式错误");
    }
    if (!plugin.getPkgVersion().matches(Constants.PLUGIN_VERSION_PATTERN)) {
      logger.error("plugin version error: " + plugin.getPkgVersion());
      erros.add("插件版本格式有误，正确格式为：${MAIN_VERSION}-${PLATVERSION}");
    }

    if (erros.size() > 0) {
      pluginManager.removePluginFile(plugin.getPkgName(), plugin.getPkgVersion());
      String error = String.join("\r\n",erros);
      throw new InvalidParamException(error);
    }

    OwnerType ownerType = OwnerType.valueOf(ownerTypeStr);
    if (ownerType == OwnerType.GROUP) {
      plugin.setOwnerType(OwnerType.GROUP);
      plugin.setOwnerId(ownerId);
    }
    plugin.setTags(tags);
    plugin.setSysCategory(sysCategory);
    plugin.setFuncCategory(funcCategory);
    plugin.setPkgId(IdUtil.uuid());
    plugin.setCreator(ContextUtil.getCurrentUser().getLoginName());
    plugin.setCreateTime(new Date());
    plugin.setUpdateTime(plugin.getCreateTime());
    plugin.setStatus(status);
    //String doc = PluginDocumentGenerator.genDocument(plugin);
    String doc = null;
    try {
      doc = pluginManager.genUsageMd(plugin);
    } catch (Exception e) {
      pluginManager.removePluginFile(plugin.getPkgName(), plugin.getPkgVersion());
      throw e;
    }

    pluginManager.savePlugin(plugin, doc);
    return getResult(true);
  }

  @RequestMapping(value = {"/admin/delete"}, method = {RequestMethod.DELETE})
  public Object removePackage(@RequestParam("pkgId") String pkgId) throws IOException {
    ResourceDetail resourceDetail = this.resourceManager.getResourceDetailById(pkgId);
    if (resourceDetail != null) {
      this.operationAccessControl.checkPermission(resourceDetail, ContextUtil.getCurrentUser(),
            PluginType.Delete);
    }
    PluginPackageV2 pluginPackage = pluginManagerV2.getPlugin(pkgId);
    pluginManagerV2.removePlugin(pluginPackage.getPkgName(), pluginPackage.getPkgVersion());
    return getResult(true);
  }

  @RequestMapping(value = "/view/types", method = RequestMethod.GET)
  public Object getPluginTypes() {
    return this.getResult(this.pluginManager.getPluginTypes());
  }

  @RequestMapping(value = "/view/versions", method = RequestMethod.GET)
  public Object getPluginVersions(@RequestParam("pkgName") String pkgName) {
    List<PluginPackageV2> pluginPackages = pluginManagerV2.getPluginsByName(pkgName);
    List<String> versions = pluginPackages.stream().map(l -> {
      return l.getPkgVersion();
    }).collect(Collectors.toList());
    return getResult(versions);
  }

  /**
   * 返回plugin的分组信息.
   *
   * @return plugin groups
   */
  @RequestMapping(value = "/view/groups", method = RequestMethod.GET)
  public Object getPluginGroups() {
    UserInfo userInfo = ContextUtil.getCurrentUser();
    List<PluginPackageViewV2> packageList = pluginManagerV2.getPlugins(userInfo.getUserId(),
          null, null, null, 0, Integer.MAX_VALUE);
    Map<String, Map<String, List<PluginBasicInfo>>> pluginGroups = new HashMap<>();
    packageList.stream().map(new Function<PluginPackageViewV2, PluginBasicInfo>() {

      @Override
      public PluginBasicInfo apply(PluginPackageViewV2 pluginPackage) {
        PluginBasicInfo basicInfo = new PluginBasicInfo();
        basicInfo.setPkgName(pluginPackage.getPkgName());
        basicInfo.setCreateTime(pluginPackage.getCreateTime());
        basicInfo.setPkgType(pluginPackage.getPkgType());
        basicInfo.setPkgVersion(pluginPackage.getPkgVersion());
        basicInfo.setPkgId(pluginPackage.getPkgId());
        basicInfo.setFuncCategory(pluginPackage.getFuncCategory());
        basicInfo.setSysCategory(pluginPackage.getSysCategory());
        basicInfo.setStatus(PluginStatus.valueOf(pluginPackage.getStatus().name()));
        basicInfo.setVersions(pluginPackage.getVersions());
        if (basicInfo.getSysCategory() == null) {
          basicInfo.setSysCategory(PluginSysCategory.Basic);
        }
        if (basicInfo.getFuncCategory() == null) {
          basicInfo.setFuncCategory(PluginSysCategory.Basic.getFuncCategories()[0]);
        }
        return basicInfo;
      }
    }).forEach(new Consumer<PluginBasicInfo>() {
      @Override
      public void accept(PluginBasicInfo pluginBasicInfo) {
        Map<String, List<PluginBasicInfo>> map = pluginGroups
              .get(pluginBasicInfo.getSysCategory().getName());
        if (map == null) {
          map = new HashMap<String, List<PluginBasicInfo>>();
          pluginGroups.put(pluginBasicInfo.getSysCategory().getName(), map);
        }
        List<PluginBasicInfo> list = map.get(pluginBasicInfo.getFuncCategory());
        if (list == null) {
          list = new ArrayList<>();
          map.put(pluginBasicInfo.getFuncCategory(), list);
        }
        list.add(pluginBasicInfo);
      }
    });
    return getResult(pluginGroups);
  }

  @RequestMapping(value = "/view/doc", method = RequestMethod.GET)
  @ResponseBody
  public Object getDocument(@RequestParam("pkgId") String pkgId) {
    UserInfo userInfo = ContextUtil.getCurrentUser();
    PluginDocumentView view =
          this.pluginManagerV2.getPluginDocumentView(userInfo.getUserId(), pkgId);
    return this.getResult(view);
  }

  @RequestMapping(value = "/admin/doc", method = RequestMethod.POST)
  public Object updateDocument(@RequestParam("pkgId") String pkgId,
        @RequestParam("userDoc") String doc) {
    UserInfo userInfo = ContextUtil.getCurrentUser();
    PluginPackage pkg = this.pluginManager.getPlugin(pkgId);
    PluginDocument document = this.pluginManagerV2
          .getPluginDocument(userInfo.getUserId(), pkg.getPkgName(),
                pkg.getPkgVersion());
    document.setDocument(doc);
    pluginManager.updateDocument(document);
    return getResult("ok");
  }

  @RequestMapping(value = "/view/hubServer")
  public Object getPluginHubServer() {
    return pluginHub;
  }

  private Map<String, Object> getPluginDetailFromHub(String name, String version)
        throws IOException {
    String url = pluginHub + "hub/v1/plugin/view/detail";
    Map<String, String> params = new HashMap<>();
    params.put("name", name);
    params.put("version", version);
    return HttpCaller.get(Map.class, url, params, null);

  }
}
